<template>
  <div class="node-create">
    <!-- ===== 通栏导航 ===== -->
    <div class="custom-header">
      <div class="header-inner">
        <div class="header-row">
          <div class="header-left" @click="goToNodeManagement">
            <svg class="back-icon" viewBox="0 0 16 16" aria-hidden="true">
              <path d="M10.5 13 6 8l4.5-5" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round"
                stroke-linejoin="round" />
            </svg>
            <span class="back-text">返回</span>
          </div>
          <span class="header-divider" aria-hidden="true"></span>
          <span class="header-title">编辑</span>
        </div>
      </div>
    </div>

    <!-- ===== 下部分：内容区域 ===== -->
    <div class="content-outer">
      <div class="content-wrap">
        <div class="content-inner">
          <div class="section-info">
            <!-- 索引名称 -->
            <div class="info-row">
              <span class="info-label">索引名称：</span>
              <span class="info-value">{{ formData.indexName }}</span>
            </div>

            <!-- 索引类型 -->
            <div class="info-row info-row--type">
              <a-form layout="inline" class="info-form">
                <a-form-item label="索引类型" class="inline-item">
                  <a-select v-model:value="formData.indexType" placeholder="请选择索引类型" :disabled="saveLoading">
                    <a-select-option value="倒排索引">倒排索引</a-select-option>
                    <a-select-option value="全文索引">全文索引</a-select-option>
                    <a-select-option :value="formData.indexType"
                      v-if="formData.indexType && !['倒排索引', '全文索引'].includes(formData.indexType)">
                      {{ formData.indexType }}
                    </a-select-option>
                  </a-select>
                </a-form-item>
              </a-form>
            </div>

            <!-- 索引权限 -->
            <div class="info-row info-row--type">
              <a-form layout="inline" class="info-form">
                <a-form-item label="索引权限" class="inline-item">
                  <a-select v-model:value="formData.indexPermission" placeholder="请选择索引权限" :disabled="saveLoading">
                    <a-select-option value="公开">公开</a-select-option>
                    <a-select-option value="私有">私有</a-select-option>
                    <a-select-option :value="formData.indexPermission"
                      v-if="formData.indexPermission && !['公开', '私有'].includes(formData.indexPermission)">
                      {{ formData.indexPermission }}
                    </a-select-option>
                  </a-select>
                </a-form-item>
              </a-form>
            </div>

            <!-- 发布时间 -->
            <div class="info-row">
              <span class="info-label">发布时间：</span>
              <span class="info-value">{{ formData.publishTime }}</span>
            </div>

            <!-- 更新状态 -->
            <div class="info-row">
              <span class="info-label">更新状态：</span>
              <span class="info-value">{{ getStatusDisplay() }}</span>
            </div>

            <!-- 文档数量 -->
            <div class="info-row">
              <span class="info-label">文档数量：</span>
              <span class="info-value">{{ formatNumber(formData.docCount) }}</span>
            </div>

            <!-- 更新时间 -->
            <div class="info-row">
              <span class="info-label">更新时间：</span>
              <span class="info-value">{{ formData.updateTime }}</span>
            </div>

            <!-- 创建人 -->
            <div class="info-row">
              <span class="info-label">创建人：</span>
              <span class="info-value">{{ formData.creator }}</span>
            </div>

            <!-- 备注 -->
            <div class="info-row">
              <span class="info-label">备注：</span>
              <span class="info-value">{{ formData.remark }}</span>
            </div>
          </div>

          <!-- 生产源列表 -->
          <section class="section section-prod">
            <div class="section-title">生产源列表</div>
            <a-table :data-source="dataSource" :loading="prodLoading || detailLoading" :columns="dataSourceColumns"
              row-key="id" class="custom-table mt-10" :pagination="false" bordered />
          </section>

          <!-- 数据同步策略 -->
          <section class="section section-sync">
            <a-form layout="vertical" class="sync-form">
              <a-form-item label="数据同步策略：">
                <a-select v-model:value="formData.syncStrategy" placeholder="请选择同步策略" class="w-400"
                  :disabled="saveLoading">
                  <a-select-option value="正则匹配">正则匹配</a-select-option>
                  <a-select-option value="其他策略">其他策略</a-select-option>
                  <a-select-option :value="formData.syncStrategy"
                    v-if="formData.syncStrategy && !['正则匹配', '其他策略'].includes(formData.syncStrategy)">
                    {{ formData.syncStrategy }}
                  </a-select-option>
                </a-select>
              </a-form-item>
            </a-form>
          </section>

          <!-- 数据同步参数 -->
          <section class="section section-sync-params">
            <a-form layout="vertical" class="sync-form">
              <a-form-item label="数据同步参数：">
                <a-input v-model:value="formData.syncExpression" placeholder="正则表达式 / JSON 参数等" class="w-400"
                  :disabled="saveLoading" />
              </a-form-item>
            </a-form>
          </section>

          <!-- 倒排索引Mapping配置 -->
          <section class="section section-mapping">
            <div class="section-title">倒排索引Mapping配置</div>

            <a-table :data-source="mappingData" :columns="mappingColumns" row-key="id" class="mapping-table mt-10"
              :pagination="false" bordered :rowClassName="rowClassName">
              <template #bodyCell="{ text, record, column }">
                <template v-if="inlineEditableCols.includes(column.key)">
                  <template v-if="isEditing(record.id, column.key)">
                    <a-input v-if="['indexFieldName', 'remark'].includes(column.key)" v-model:value="editingValue"
                      size="small" autofocus @blur="onEditBlur(record)" @pressEnter="onEditBlur(record)"
                      @keydown.esc.prevent="onEditCancel" :disabled="saveLoading || !rowEditable(record)" />
                    <a-input-number v-else v-model:value="editingNumber" :min="0" :max="100" :precision="0"
                      :controls="false" size="small" autofocus class="w-120" placeholder="0-100"
                      @blur="onEditBlur(record)" @pressEnter="onEditBlur(record)" @keydown.esc.prevent="onEditCancel"
                      :disabled="saveLoading || !rowEditable(record)" />
                  </template>
                  <template v-else>
                    <span class="editable-cell"
                      @click="!saveLoading && rowEditable(record) && startEdit(record, column.key)"
                      :style="{ cursor: (!saveLoading && rowEditable(record)) ? 'text' : 'not-allowed' }">
                      {{ record[column.key] ?? '—' }}
                    </span>
                  </template>
                </template>

                <!-- 下拉选择列超过8条出现滚动条 -->
                <template v-else-if="dropdownCols.includes(column.key)">
                  <a-dropdown trigger="['click']">
                    <span class="datatype-trigger" @click.prevent :style="{
                      cursor: (saveLoading || (column.key !== 'isIndexed' && !rowEditable(record))) ? 'not-allowed' : 'pointer',
                      opacity: (column.key !== 'isIndexed' && !rowEditable(record)) ? .65 : 1
                    }">
                      <template v-if="record[column.key]">
                        <span class="datatype-text" :class="{
                          'bool-true': ['isIndexed', 'isStored'].includes(column.key) && record[column.key] === '是',
                          'bool-false': ['isIndexed', 'isStored'].includes(column.key) && record[column.key] === '否'
                        }">{{ record[column.key] }}</span>
                        <span class="triangle"></span>
                      </template>
                      <template v-else>
                        <span class="datatype-text">请选择</span>
                        <span class="triangle triangle-only"></span>
                      </template>
                    </span>
                    <template #overlay>
                      <a-menu :style="{ maxHeight: '224px', overflowY: 'auto' }" @click="({ key }) => {
                        if (!saveLoading && (column.key === 'isIndexed' || rowEditable(record))) {
                          onDropdownSelect(record, column.key, key as string)
                        }
                      }">
                        <a-menu-item v-for="opt in dropdownOptions[column.key]" :key="opt">
                          {{ opt }}
                        </a-menu-item>
                      </a-menu>
                    </template>
                  </a-dropdown>
                </template>

                <!-- 默认展示 -->
                <span v-else>{{ text }}</span>
              </template>
            </a-table>

            <!-- 向量索引Mapping配置 -->
            <div class="section-title mt-16">向量索引Mapping配置</div>
            <a-table :data-source="vectorMappingData" :columns="vectorMappingColumns" row-key="_rid"
              class="mapping-table vector-table mt-10" :pagination="false" bordered :rowClassName="vectorRowClassName">
              <template #bodyCell="{ text, record, column }">
                <!-- 下拉选择列（>8 出滚动条） -->
                <template v-if="vectorDropdownCols.includes(column.key)">
                  <a-dropdown trigger="['click']">
                    <span class="datatype-trigger" @click.prevent :style="{
                      cursor: (saveLoading || (column.key !== 'isIndexed' && !vectorRowEditable(record))) ? 'not-allowed' : 'pointer',
                      opacity: (column.key !== 'isIndexed' && !vectorRowEditable(record)) ? .65 : 1
                    }">
                      <template v-if="record[column.key] !== undefined && record[column.key] !== ''">
                        <span class="datatype-text" :class="{
                          'bool-true': column.key === 'isIndexed' && record[column.key] === '是',
                          'bool-false': column.key === 'isIndexed' && record[column.key] === '否'
                        }">{{ record[column.key] }}</span>
                        <span class="triangle"></span>
                      </template>
                      <template v-else>
                        <span class="datatype-text">请选择</span>
                        <span class="triangle triangle-only"></span>
                      </template>
                    </span>
                    <template #overlay>
                      <a-menu :style="{ maxHeight: '224px', overflowY: 'auto' }" @click="({ key }) => {
                        if (!saveLoading && (column.key === 'isIndexed' || vectorRowEditable(record))) {
                          onVectorDropdownSelect(record, column.key, key as string)
                        }
                      }">
                        <a-menu-item v-for="opt in vectorDropdownOptions[column.key]" :key="opt">
                          {{ opt }}
                        </a-menu-item>
                      </a-menu>
                    </template>
                  </a-dropdown>
                </template>

                <!-- 行内可编辑列 -->
                <template v-else-if="vectorInlineEditableCols.includes(column.key)">
                  <template v-if="isVectorEditing(record._rid, column.key)">
                    <a-input v-if="['indexFieldName', 'vectorAlgorithm', 'remark'].includes(column.key)"
                      v-model:value="vectorEditingValue" size="small" autofocus @blur="onVectorEditBlur(record)"
                      @pressEnter="onVectorEditBlur(record)" @keydown.esc.prevent="onVectorEditCancel"
                      :disabled="saveLoading || !vectorRowEditable(record)" />
                    <a-input-number v-else v-model:value="vectorEditingNumber" :min="0" :precision="0" :controls="false"
                      size="small" autofocus class="w-120" placeholder="0+" @blur="onVectorEditBlur(record)"
                      @pressEnter="onVectorEditBlur(record)" @keydown.esc.prevent="onVectorEditCancel"
                      :disabled="saveLoading || !vectorRowEditable(record)" />
                  </template>
                  <template v-else>
                    <span class="editable-cell"
                      @click="!saveLoading && vectorRowEditable(record) && startVectorEdit(record, column.key)"
                      :style="{ cursor: (!saveLoading && vectorRowEditable(record)) ? 'text' : 'not-allowed' }">
                      {{ record[column.key] ?? '—' }}
                    </span>
                  </template>
                </template>

                <!-- 默认展示 -->
                <span v-else>{{ text }}</span>
              </template>
            </a-table>

            <!-- 按钮 -->
            <div class="actions-bar mt-16">
              <a-button class="btn-cancel" @click="handleCancelMapping" :disabled="saveLoading">取 消</a-button>
              <a-button type="primary" class="btn-save" :disabled="saveLoading" @click="handleSave">保 存</a-button>
            </div>
          </section>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { reactive, ref, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import { message } from "ant-design-vue";
import Http from "@/api";

const router = useRouter();
const route = useRoute();

/* ===== 创建人 ===== */
const microCname: string =
  (window as any)?.__MicroAPP__?.__user?.userInfo?.cname ||
  (window as any)?.__MicroAPP__?.user?.userInfo?.cname ||
  "";

/* ========== 基础信息 ========== */
const formData = reactive({
  indexName: "",
  indexType: "",
  indexPermission: "", // 索引权限
  publishTime: "",
  updateTime: "",
  creator: "", // 创建人
  remark: "",
  syncStrategy: "",
  syncExpression: "",
  updateStatus: "", // 更新状态文本
  syncProgress: "", // 进度
  docCount: 0, // 文档数量
  indexUpdateStrategy: "",
  indexUpdateTimeStrategy: "",
});

const indexId = ref<string>("");
const sourceId = ref<string>("");

const formatYMDHMS = (input: any) => {
  if (!input) return "";
  const d = new Date(input);
  if (isNaN(d.getTime())) return String(input);
  const pad = (n: number) => (n < 10 ? `0${n}` : `${n}`);
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(
    d.getMinutes()
  )}:${pad(d.getSeconds())}`;
};

/* 展示“是/否” */
const ynToBoolStr = (v: any) =>
  v === "Y" || v === "y" || v === true || v === "true" ? "是"
    : v === "N" || v === "n" || v === false || v === "false" ? "否"
      : String(v ?? "");

/* 保存映射仍传 Y/N（兼容“是/否/true/false/布尔”） */
const toYN = (v: any) => {
  if (v === "是" || v === true || v === "true" || v === "Y" || v === "y") return "Y";
  if (v === "否" || v === false || v === "false" || v === "N" || v === "n") return "N";
  return "";
};

/* 文档数量 */
const formatNumber = (n: any) => {
  const x = Number(n);
  return Number.isFinite(x) ? x.toLocaleString() : "—";
};
/* 更新状态 */
const getStatusDisplay = () => {
  const raw = String(formData.updateStatus || "").trim();
  const p = String(formData.syncProgress || "").trim();
  const pct = p.replace(/[^\d.]/g, "");
  const progressPart = pct ? ` ${Math.min(100, Math.max(0, Number(pct)))}%` : "";
  if (!raw && !progressPart) return "—";
  const map: Record<string, string> = {
    running: "进行中",
    processing: "进行中",
    进行中: "进行中",
    syncing: "进行中",
    done: "已完成",
    finished: "已完成",
    success: "已完成",
    已完成: "已完成",
    failed: "失败",
    error: "失败",
    失败: "失败",
  };
  const key = raw.toLowerCase();
  const base = map[raw] || map[key] || raw;
  return `${base}${progressPart}`.trim();
};

/* 路由预回显 */
const initData = () => {
  const q = route.query as Record<string, any>;
  indexId.value = String(q.index_id ?? "");
  formData.indexName = q.index_name ?? "未定义";
  formData.indexType = q.index_type ?? "未定义";
  formData.indexPermission = q.index_permission ?? q.index_perm ?? "";
  formData.publishTime = q.create_time ? formatYMDHMS(q.create_time) : "—";
  formData.updateTime = q.update_time ? formatYMDHMS(q.update_time) : "—";
  // 创建人
  formData.creator = microCname || q.index_owner_id || "—";
  formData.remark = q.memo ?? "—";
  formData.syncStrategy = q.datasync_strategy ?? "";

  // 进度/状态/数量
  formData.updateStatus =
    q.datasync_message ?? q.update_status ?? q.sync_status ?? q.index_state_text ?? "";
  formData.syncProgress = q.progress ?? q.sync_progress ?? q.progress_percent ?? "";
  formData.docCount = Number(q.doc_count ?? 0) || 0;

  let paras = q.datasync_paras ?? "";
  try { paras = decodeURIComponent(String(paras)); } catch { }
  try {
    const maybe = JSON.parse(paras);
    formData.syncExpression = typeof maybe === "object" ? JSON.stringify(maybe) : String(paras);
  } catch {
    formData.syncExpression = String(paras);
  }

  formData.indexUpdateStrategy = q.index_update_strategy ?? "";
  formData.indexUpdateTimeStrategy = q.index_update_time_strategy ?? "";
};

/* 接口回填基础信息 */
const patchBaseInfo = (info: any) => {
  if (!info) return;
  formData.indexName = info.index_name ?? formData.indexName;
  formData.indexType = info.index_type ?? formData.indexType;
  formData.indexPermission = info.index_permission ?? info.index_perm ?? formData.indexPermission;
  formData.publishTime = info.create_time ? formatYMDHMS(info.create_time) : formData.publishTime || "—";
  formData.updateTime = info.update_time ? formatYMDHMS(info.update_time) : formData.updateTime || "—";
  // 创建人
  formData.creator = microCname || info.index_owner_id || info.index_owner || formData.creator || "—";
  formData.remark = info.memo ?? formData.remark;
  formData.syncStrategy = info.datasync_strategy ?? formData.syncStrategy;

  // 进度/状态/数量
  formData.updateStatus =
    info.datasync_message ?? info.update_status ?? info.sync_status ?? info.index_state_text ?? formData.updateStatus;
  formData.syncProgress = info.progress ?? info.sync_progress ?? info.progress_percent ?? formData.syncProgress;
  formData.docCount = Number(info.doc_count ?? formData.docCount) || formData.docCount;

  formData.indexUpdateStrategy = info.index_update_strategy ?? formData.indexUpdateStrategy;
  formData.indexUpdateTimeStrategy = info.index_update_time_strategy ?? formData.indexUpdateTimeStrategy;

  sourceId.value = String(info.source_id ?? "");
  fetchProdSource(sourceId.value);
};

const goToNodeManagement = () => router.back();

/* ========== 生产源列表 ========== */
const dataSource = reactive<any[]>([]);
const prodLoading = ref(false);

/* 数据类型 */
const dataSourceColumns = [
  { title: "ID", dataIndex: "id", key: "id", align: "center" },
  {
    title: "生产源名称",
    dataIndex: "name",
    key: "name",
    align: "center",
    sorter: (a: any, b: any) => String(a.name ?? "").localeCompare(String(b.name ?? "")),
  },
  {
    title: "数据类型",
    dataIndex: "dataModel",
    key: "dataModel",
    align: "center",
    sorter: (a: any, b: any) => String(a.dataModel ?? "").localeCompare(String(b.dataModel ?? "")),
  },
  {
    title: "数据量",
    dataIndex: "count",
    key: "count",
    align: "center",
    sorter: (a: any, b: any) => Number(a.count ?? 0) - Number(b.count ?? 0),
  },
  {
    title: "更新时间",
    dataIndex: "updateTime",
    key: "updateTime",
    align: "center",
    sorter: (a: any, b: any) =>
      new Date(a.updateTime ?? 0).getTime() - new Date(b.updateTime ?? 0).getTime(),
  },
  {
    title: "同步状态",
    dataIndex: "status",
    key: "status",
    align: "center",
    sorter: (a: any, b: any) => String(a.status ?? "").localeCompare(String(b.status ?? "")),
  },
];

/* 回填生产源数据 */
const patchProdSource = (list: any[]) => {
  if (!Array.isArray(list)) return;
  const rows = list.map((it: any, idx: number) => {
    const id = it.id ?? it.source_id ?? it.prod_id ?? idx + 1;
    const name = it.prod_source_name ?? it.name ?? it.source_name ?? it.prod_name ?? "—";
    const dataModel = it.data_model ?? it.datamodel ?? it.dataModel ?? "—";
    const count = Number(it.data_count ?? it.count ?? it.size ?? it.total ?? it.total_count ?? 0);
    const updateTimeRaw =
      it.update_time ?? it.updated_at ?? it.updateTime ?? it.last_update_time;
    const updateTime = updateTimeRaw ? formatYMDHMS(updateTimeRaw) : "—";
    const status = String(it.status ?? it.sync_status ?? "—");
    return { id, name, dataModel, count, updateTime, status };
  });
  dataSource.splice(0, dataSource.length, ...rows);
};

const fetchProdSource = async (sid: string) => {
  const sidStr = String(sid ?? "").trim();
  if (!sidStr) {
    dataSource.splice(0, dataSource.length);
    return;
  }
  prodLoading.value = true;
  try {
    const res: any = await Http.getProdSourceInfo({ sourceId: sidStr });
    const payload = res?.data?.data ?? res?.data ?? res;
    const list = Array.isArray(payload)
      ? payload
      : payload && typeof payload === "object"
        ? [payload]
        : [{ id: sidStr, prod_source_name: "", data_model: "", data_count: 0, update_time: null, status: "" }];
    patchProdSource(list);
  } finally {
    prodLoading.value = false;
  }
};

/* ========== Mapping（倒排索引） ========== */
const mappingData = reactive<any[]>([]);
const mappingColumns = [
  { title: "生产源字段名称", dataIndex: "fieldName", key: "fieldName", align: "center" },
  { title: "数据类型", dataIndex: "dataType", key: "dataType", align: "center" },
  { title: "是否索引", dataIndex: "isIndexed", key: "isIndexed", align: "center" },
  { title: "索引字段名称", dataIndex: "indexFieldName", key: "indexFieldName", align: "center" },
  { title: "索引字段类型", dataIndex: "indexType", key: "indexType", align: "center" },
  { title: "是否存储", dataIndex: "isStored", key: "isStored", align: "center" },
  { title: "分词器", dataIndex: "tokenizer", key: "tokenizer", align: "center" },
  { title: "分词词典", dataIndex: "defaultDict", key: "defaultDict", align: "center" },
  { title: "同义词词典", dataIndex: "synonymDict", key: "synonymDict", align: "center" },
  { title: "内容转换规则", dataIndex: "contentRule", key: "contentRule", align: "center" },
  { title: "权重", dataIndex: "weight", key: "weight", align: "center" },
  { title: "备注", dataIndex: "remark", key: "remark", align: "center" },
];
/* 下拉以外可内联编辑的列 */
const inlineEditableCols = ["indexFieldName", "weight", "remark"] as const;

/* —— 新增：行是否可编辑判断 —— */
const rowEditable = (record: any) => record.isIndexed === "是";
const rowClassName = (record: any) => (rowEditable(record) ? "" : "row-disabled");

const editing = ref<{ id: number | null; field: string | null }>({ id: null, field: null });
const editingValue = ref<string>("");
const editingNumber = ref<number | null>(0);
let snapshot: any = null;

const isEditing = (id: number, field: string) =>
  editing.value.id === id && editing.value.field === field;

const startEdit = (record: any, field: string) => {
  editing.value = { id: record.id, field };
  snapshot = { indexFieldName: record.indexFieldName, weight: record.weight, remark: record.remark };
  if (["indexFieldName", "remark"].includes(field)) {
    editingValue.value = record[field] ?? "";
  } else {
    const n = Number(record.weight);
    editingNumber.value = Number.isFinite(n) ? Math.max(0, Math.min(100, Math.round(n))) : 0;
  }
};

const onEditBlur = (record: any) => {
  if (!editing.value.field) return;
  if (["indexFieldName", "remark"].includes(editing.value.field!)) {
    record[editing.value.field!] = (editingValue.value ?? "").trim();
  } else {
    let n = Number(editingNumber.value);
    if (!Number.isFinite(n)) n = 0;
    record.weight = Math.max(0, Math.min(100, Math.round(n)));
  }
  editing.value = { id: null, field: null };
  snapshot = null;
};

const onEditCancel = () => {
  if (!editing.value.id || !editing.value.field || !snapshot) {
    editing.value = { id: null, field: null };
    return;
  }
  const row = mappingData.find((r) => r.id === editing.value!.id);
  if (row) Object.assign(row, snapshot);
  editing.value = { id: null, field: null };
  snapshot = null;
};

/* 下拉列（倒排索引） */
const dropdownCols = [
  "fieldName",
  "dataType",
  "indexType",
  "isIndexed",
  "isStored",
  "tokenizer",
  "defaultDict",
  "synonymDict",
  "contentRule",
] as const;
type DropdownKey = (typeof dropdownCols)[number];

/* 生产源字段名称的下拉选项 */
const fieldNameOptions = ref<string[]>([]);
/* 统一的下拉选项对象（倒排索引） */
const dropdownOptions: Record<DropdownKey, string[]> = {
  fieldName: fieldNameOptions.value,
  dataType: ["VARCHAR", "TEXT", "DATE", "INT", "FLOAT", "BOOLEAN", "Keyword"],
  indexType: ["TEXT", "Keyword", "DATE", "NUMERIC"],
  /* 改为“是/否” */
  isIndexed: ["是", "否"],
  isStored: ["是", "否"],
  tokenizer: ["标准分词器", "智能分词器", "英文分词器", "不分词"],
  defaultDict: ["默认分库", "通用词典", "行业词典-军事", "行业词典-财经", "自定义词典A", "自定义词典B"],
  synonymDict: ["默认词库", "公共词库", "军事同义词", "财经同义词", "无"],
  contentRule: ["请选择", "HTML去标签", "小写化", "去停用词", "正则清洗"],
};

const onDropdownSelect = (record: any, field: string, value: string) => {
  if (record[field] !== value) record[field] = value;
};

/* 映射回显（倒排索引） */
const patchMapping = (list: any[]) => {
  if (!Array.isArray(list)) return;
  const rows = list.map((it: any, idx: number) => {
    const id = it.id ?? it.index_field_no ?? idx + 1;
    const fieldName = it.source_field_name ?? `field_${idx + 1}`;
    const indexFieldName = it.index_field_name ?? "";
    const dataType = (it.source_field_type ?? "").toString().toUpperCase();
    const indexType = (it.index_field_type ?? "").toString().toUpperCase();
    const contentRule = it.field_if_mapping ?? it.content_rule ?? "请选择";
    const rawWeight = it.index_weight ?? it.weight;
    const weight = Number.isFinite(+rawWeight) ? +rawWeight : 0;
    const remark = it.memo ?? "";
    return {
      id,
      fieldName,
      indexFieldName,
      dataType,
      indexType,
      isIndexed: ynToBoolStr(it.if_index),   // “是/否”
      isStored: ynToBoolStr(it.if_store),    // “是/否”
      tokenizer: it.tokenizer ?? "",
      defaultDict: it.tokenizer_dict_id ?? "",
      synonymDict: it.synonym_dict_id ?? "",
      contentRule,
      weight,
      remark,
    };
  });
  mappingData.splice(0, mappingData.length, ...rows);

  // 生成“生产源字段名称”的下拉候选
  const opts = Array.from(new Set(rows.map((r) => r.fieldName).filter(Boolean)));
  fieldNameOptions.value = opts;
  dropdownOptions.fieldName = fieldNameOptions.value;
};

/* ========== 向量索引 ========== */
const vectorMappingData = reactive<any[]>([]);
const vectorMappingColumns = [
  { title: "生产源字段名称", dataIndex: "sourceFieldName", key: "sourceFieldName", align: "center" },
  { title: "数据类型", dataIndex: "sourceFieldType", key: "sourceFieldType", align: "center" },
  { title: "是否索引", dataIndex: "isIndexed", key: "isIndexed", align: "center" },
  { title: "索引字段名称", dataIndex: "indexFieldName", key: "indexFieldName", align: "center" },
  { title: "索引类型", dataIndex: "indexFieldType", key: "indexFieldType", align: "center" },
  { title: "向量维度", dataIndex: "vectorDimension", key: "vectorDimension", align: "center" },
  { title: "向量化算法", dataIndex: "vectorAlgorithm", key: "vectorAlgorithm", align: "center" },
  { title: "权重", dataIndex: "weight", key: "weight", align: "center" },
  { title: "备注", dataIndex: "remark", key: "remark", align: "center" },
];

/* —— 向量表：下拉与行内编辑（仅本表） —— */
const vectorSourceFieldNameOptions = ref<string[]>([]);

/* 需要下拉的列（向量表） */
const vectorDropdownCols = ["sourceFieldName", "sourceFieldType", "isIndexed", "indexFieldType"] as const;
type VectorDropdownKey = (typeof vectorDropdownCols)[number];

/* 下拉项（向量表） */
const vectorDropdownOptions: Record<VectorDropdownKey, string[]> = {
  sourceFieldName: vectorSourceFieldNameOptions.value,
  sourceFieldType: ["VARCHAR", "TEXT", "DATE", "INT", "FLOAT", "BOOLEAN", "Keyword"],
  /* 改为“是/否” */
  isIndexed: ["是", "否"],
  indexFieldType: ["TEXT", "Keyword", "DATE", "NUMERIC"],
};

/* 需要行内编辑的列（向量表） */
const vectorInlineEditableCols = ["indexFieldName", "vectorDimension", "vectorAlgorithm", "weight", "remark"] as const;

/* —— 新增：行是否可编辑判断（向量表） —— */
const vectorRowEditable = (record: any) => record.isIndexed === "是";
const vectorRowClassName = (record: any) => (vectorRowEditable(record) ? "" : "row-disabled");

/* 编辑状态（向量表） */
const vectorEditing = ref<{ id: string | null; field: string | null }>({ id: null, field: null });
const vectorEditingValue = ref<string>("");
const vectorEditingNumber = ref<number | null>(0);
let vectorSnapshot: any = null;

const isVectorEditing = (rid: string, field: string) =>
  vectorEditing.value.id === rid && vectorEditing.value.field === field;

const startVectorEdit = (record: any, field: string) => {
  vectorEditing.value = { id: record._rid, field };
  vectorSnapshot = {
    indexFieldName: record.indexFieldName,
    vectorDimension: record.vectorDimension,
    vectorAlgorithm: record.vectorAlgorithm,
    weight: record.weight,
    remark: record.remark,
  };
  if (["indexFieldName", "vectorAlgorithm", "remark"].includes(field)) {
    vectorEditingValue.value = record[field] ?? "";
  } else {
    const n = Number(record[field]);
    vectorEditingNumber.value = Number.isFinite(n) ? Math.max(0, Math.round(n)) : 0;
  }
};

const onVectorEditBlur = (record: any) => {
  if (!vectorEditing.value.field) return;
  const f = vectorEditing.value.field!;
  if (["indexFieldName", "vectorAlgorithm", "remark"].includes(f)) {
    record[f] = (vectorEditingValue.value ?? "").trim();
  } else {
    let n = Number(vectorEditingNumber.value);
    if (!Number.isFinite(n)) n = 0;
    record[f] = Math.max(0, Math.round(n));
  }
  vectorEditing.value = { id: null, field: null };
  vectorSnapshot = null;
};

const onVectorEditCancel = () => {
  if (!vectorEditing.value.id || !vectorEditing.value.field || !vectorSnapshot) {
    vectorEditing.value = { id: null, field: null };
    return;
  }
  const row = vectorMappingData.find((r) => r._rid === vectorEditing.value!.id);
  if (row) Object.assign(row, vectorSnapshot);
  vectorEditing.value = { id: null, field: null };
  vectorSnapshot = null;
};

/* 下拉选择（向量表） */
const onVectorDropdownSelect = (record: any, field: string, value: string) => {
  if (record[field] !== value) record[field] = value;
};

const patchVectorMapping = (list: any[]) => {
  if (!Array.isArray(list)) return;
  const rows = list.map((it: any, idx: number) => ({
    _rid: `${it.index_field_no ?? 'rid'}-${idx}`,        // 唯一键
    id: it.index_field_no ?? idx + 1,
    indexId: it.index_id ?? "",
    sourceFieldName: it.source_field_name ?? "",
    sourceFieldType: (it.source_field_type ?? "").toString(),
    isIndexed: ynToBoolStr(it.if_index),   // “是/否”
    indexFieldName: it.index_field_name ?? "",
    indexFieldType: (it.index_field_type ?? "").toString(),
    vectorDimension: Number(it.vector_dimension ?? 0),
    vectorAlgorithm: it.vector_algorithm ?? "",
    weight: Number(it.weight ?? it.index_weight ?? 0),
    remark: it.memo ?? "",
  }));
  vectorMappingData.splice(0, vectorMappingData.length, ...rows);

  // 生产源字段名称下拉候选（>8 时菜单自动滚动）
  const opts = Array.from(new Set(rows.map((r) => r.sourceFieldName).filter(Boolean)));
  vectorSourceFieldNameOptions.value = opts;
  vectorDropdownOptions.sourceFieldName = vectorSourceFieldNameOptions.value;
};

/* ========== 保存/取消 ========== */
const saveLoading = ref(false);
const originalMapping = ref<any[]>([]);
const deepClone = <T>(v: T): T => JSON.parse(JSON.stringify(v));

const initialSnapshot = ref<{ form: any; mapping: any[] } | null>(null);
const takeInitialSnapshot = () => {
  if (!initialSnapshot.value) {
    initialSnapshot.value = { form: deepClone(formData), mapping: deepClone(mappingData) };
  }
};
const restoreInitial = () => {
  if (initialSnapshot.value) {
    Object.assign(formData, deepClone(initialSnapshot.value.form));
    mappingData.splice(0, mappingData.length, ...deepClone(initialSnapshot.value.mapping));
    editing.value = { id: null, field: null };
    snapshot = null;
  }
};

const buildSavePayload = () => {
  const idxIdNum = Number(indexId.value || 0);
  const normalize = (v: any) => (v === undefined || v === null || v === "请选择" ? "" : String(v));

  // 倒排索引 mapping
  const items = mappingData.map((r, i) => ({
    index_field_no: String(r.id ?? i + 1),
    index_id: idxIdNum ? String(idxIdNum) : "",
    index_field_name: normalize(r.indexFieldName),
    field_if_mapping: normalize(r.contentRule),
    content_rule: normalize(r.contentRule),
    source_field_name: normalize(r.fieldName),
    source_field_type: normalize(r.dataType),
    index_field_type: normalize(r.indexType),
    if_index: toYN(r.isIndexed),
    if_store: toYN(r.isStored),
    tokenizer: normalize(r.tokenizer),
    tokenizer_dict_id: normalize(r.defaultDict),
    synonym_dict_id: normalize(r.synonymDict),
    index_weight: Number.isFinite(Number(r.weight)) ? Number(r.weight) : 0,
    memo: normalize(r.remark),
    operator_id: "",
  }));

  // 向量索引 mapping
  const vectorItems = vectorMappingData.map((r: any, i: number) => ({
    index_field_no: String(r.id ?? i + 1),
    index_id: idxIdNum ? String(idxIdNum) : "",
    source_field_name: normalize(r.sourceFieldName),
    source_field_type: normalize(r.sourceFieldType),
    if_index: toYN(r.isIndexed),
    index_field_name: normalize(r.indexFieldName),
    index_field_type: normalize(r.indexFieldType),
    vector_dimension: Number.isFinite(Number(r.vectorDimension)) ? Number(r.vectorDimension) : 0,
    vector_algorithm: normalize(r.vectorAlgorithm),
    index_weight: Number.isFinite(Number(r.weight)) ? Number(r.weight) : 0,
    memo: normalize(r.remark),
  }));

  return {
    index_id: Number.isFinite(idxIdNum) ? idxIdNum : 0,
    index_name: formData.indexName ?? "",
    index_owner_id: formData.creator ?? "",
    index_owner_group: "",
    index_type: formData.indexType ?? "",
    index_permission: formData.indexPermission ?? "",
    memo: formData.remark ?? "",
    source_id: sourceId.value ?? "",
    datasync_strategy: formData.syncStrategy ?? "",
    datasync_paras: String(formData.syncExpression ?? ""),
    index_state: 0,
    index_mapping_items: items,
    vector_mapping_items: vectorItems,
  };
};

const handleSave = async () => {
  const payload = buildSavePayload();
  saveLoading.value = true;
  try {
    await Http.getCreateIndex(payload);
    message.success("保存成功");
    router.push("/");
  } catch (e) {
    message.error("保存失败");
  } finally {
    saveLoading.value = false;
  }
};

const handleCancelMapping = () => {
  restoreInitial();
  router.back();
};

/* 详情拉取并回显 */
const detailLoading = ref(false);
const fetchDetail = async () => {
  if (!indexId.value) return;
  detailLoading.value = true;
  try {
    const res: any = await Http.getIndexById({ indexId: indexId.value });
    const data = (res?.data?.data ?? res?.data ?? res) as any;
    patchBaseInfo(data?.index_info);
    patchMapping(data?.mapping_list || data?.index_mapping_items || []);
    patchVectorMapping(data?.vector_mapping_list || []);
  } finally {
    detailLoading.value = false;
  }
};

onMounted(async () => {
  initData();
  await fetchDetail();
  takeInitialSnapshot();
});
</script>

<style scoped lang="less">
/* ===== 基础变量 ===== */
@border-light: #f0f0f0;
@page-bg: #f0f2f5;
@text-strong: #262626;
@text-secondary: #595959;
@font-base: "PingFang SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
  "Helvetica Neue", Arial, "Noto Sans", "Microsoft YaHei", sans-serif;
@brand-blue: #000;
@section-gap: 24px;
@table-row-h: 40px;
@thead-bg-main: #f4f8fd;

@bool-true-color: #52c41a;
@bool-false-color: #1f1f1f;

:root {
  --text-secondary-858b9b: #858b9b;
  --text-strong-2a2f3c: #2a2f3c;
  --label-color-858b9b: var(--text-secondary-858b9b);
  --value-color-2a2f3c: var(--text-strong-2a2f3c);
}

/* ===== 常用混入 ===== */
.font-regular() {
  font-family: @font-base;
  font-weight: 400;
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0;
}

.section-title-regular() {
  .font-regular();
}

.ant-vertical-66() {
  margin: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 66px;
}

.ant-typography-regular() {
  color: var(--value-color-2a2f3c, #2a2f3c);
  .font-regular();
}

/* 表格紧凑 */
.table-compact(@theadBg: @thead-bg-main, @rowH: @table-row-h) {
  :deep(.ant-table-thead > tr > th) {
    background: @theadBg !important;
    height: @rowH !important;
    padding-top: (@rowH - 20px) / 2 !important;
    padding-bottom: (@rowH - 20px) / 2 !important;
    line-height: 20px !important;
    text-align: center;
  }

  :deep(.ant-table-tbody > tr > td) {
    height: @rowH !important;
    padding-top: (@rowH - 20px) / 2 !important;
    padding-bottom: (@rowH - 20px) / 2 !important;
    line-height: 20px !important;
    vertical-align: middle;
  }

  :deep(.ant-table-column-sorters) {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
  }
}

/* 页面容器 */
.node-create {
  --page-padding-x: 10px;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  padding: 0 var(--page-padding-x) 24px;
  background: @page-bg;
}

/* 顶部导航 */
.custom-header {
  flex: 0 0 56px;
  height: 56px;
  background: #fff;
  border-bottom: 1px solid @border-light;
  margin-left: calc(-1 * var(--page-padding-x));
  margin-right: calc(-1 * var(--page-padding-x));
  width: calc(100% + var(--page-padding-x) * 2);
}

.header-inner {
  height: 100%;
  padding: 0 16px;
  display: flex;
  align-items: center;
}

.header-row {
  display: inline-flex;
  align-items: center;
}

.header-left {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
  color: #8c8c8c;
}

.back-icon {
  width: 12px;
  height: 12px;
  margin-left: 16px;
}

.back-text {
  font-family: @font-base;
  font-weight: 400;
  font-size: 14px;
  line-height: 100%;
  letter-spacing: 0;
  color: @text-secondary;
}

.header-divider {
  width: 1px;
  height: 16px;
  background: #d9d9d9;
  margin-left: 16px;
  display: inline-block;
}

.header-title {
  margin-left: 16px;
  font-family: @font-base;
  font-weight: 500;
  font-size: 16px;
  line-height: 100%;
  letter-spacing: 0;
  color: @text-strong;
}

/* 主体 */
.content-outer {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  margin-top: 10px;
  min-height: 0;
}

.content-wrap {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
  flex: 1 1 auto;
}

.content-inner {
  display: flex;
  flex-direction: column;
  gap: @section-gap;
  padding: 10px 24px 0 24px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  min-height: 0;
  flex: 1 1 auto;
}

/* 基本信息 */
.section-info {
  display: flex;
  flex-direction: column;
  row-gap: 16px;
  margin-top: 14px;
}

.info-row {
  display: flex;
  align-items: center;
  min-height: 22px;
  line-height: 22px;
}

.info-label {
  margin-right: 10px;
  .font-regular();
  color: var(--label-color-858b9b, #858b9b) !important;
}

.info-value {
  .font-regular();
  line-height: 22px;
  color: var(--value-color-2a2f3c, #2a2f2c) !important;
}

.info-row--type {
  min-height: 40px;
  align-items: center;
}

:deep .ant-select .ant-select-selection-item {
  line-height: 30px !important;
}

.info-form {
  width: 100%;
  display: flex;
  align-items: center;

  :deep(.ant-form-item) {
    margin: 0 !important;
  }

  :deep(.ant-form-item-label > label) {
    .ant-typography-regular();
  }

  :deep(.ant-select .ant-select-selector),
  :deep(.ant-select .ant-select-selection-item),
  :deep(.ant-select .ant-select-selection-placeholder) {
    .ant-typography-regular();
  }
}

.inline-item :deep(.ant-form-item-control) {
  min-width: 420px;
}

.inline-item :deep(.ant-select) {
  width: 420px !important;
}

/* 生产源列表 */
.section-prod {
  .section-title {
    .section-title-regular();
  }

  .custom-table {
    .table-compact(@thead-bg-main, @table-row-h);

    :deep(.ant-table-thead > tr > th) {
      color: #81889c !important;
    }

    /* 生产源名称列蓝色（表体） */
    :deep(.ant-table-tbody > tr > td:nth-child(2)) {
      color: @brand-blue;
    }
  }
}

.section-sync .sync-form,
.section-sync-params .sync-form {
  width: 100%;
  display: flex;
  align-items: center;
}

.section-sync .sync-form :deep(.ant-form-item),
.section-sync-params .sync-form :deep(.ant-form-item) {
  .ant-vertical-66();
}

.section-sync .sync-form :deep(.ant-form-item-label > label),
.section-sync-params .sync-form :deep(.ant-form-item-label > label) {
  .section-title-regular();
  color: var(--value-color-2a2f3c, #2a2f3c);
}

.section-sync .sync-form :deep(.ant-select .ant-select-selector),
.section-sync .sync-form :deep(.ant-select .ant-select-selection-item),
.section-sync .sync-form :deep(.ant-select .ant-select-selection-placeholder),
.section-sync-params .sync-form :deep(.ant-select .ant-select-selector),
.section-sync-params .sync-form :deep(.ant-select .ant-select-selection-item),
.section-sync-params .sync-form :deep(.ant-select .ant-select-selection-placeholder),
.section-sync .sync-form :deep(.ant-input),
.section-sync-params .sync-form :deep(.ant-input) {
  .ant-typography-regular();
}

/* 通用区块与表格 */
.section {
  display: flex;
  flex-direction: column;
}

.section-title {
  font-size: 16px;
  font-weight: 500;
  color: @text-strong;
}

.custom-table,
.mapping-table {
  overflow: auto;

  :deep(.ant-table) {
    min-width: 720px;
  }

  :deep(.ant-table-thead > tr > th) {
    background-color: #e6f7ff;
    font-weight: 500;
  }

  :deep(.ant-table-column-sorters) {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0;
  }
}

.mapping-table :deep(.ant-table-cell) {
  text-align: center;
}

/* Mapping */
.section-mapping {
  .section-title {
    .section-title-regular();
  }

  .mapping-table,
  .mapping-table :deep(.ant-table),
  .mapping-table :deep(.ant-table-thead > tr > th),
  .mapping-table :deep(.ant-table-tbody > tr > td),
  .editable-cell,
  .datatype-trigger,
  .datatype-text {
    .font-regular();
    color: var(--value-color-2a2f3c, #2a2f3c);
  }

  .mapping-table {
    .table-compact(@thead-bg-main, @table-row-h);
  }
}

.section-mapping .vector-table :deep(.ant-table-thead > tr > th) {
  background-color: #f4f8fd !important;
}

.editable-cell {
  cursor: text;
  display: inline-block;
  min-height: 22px;
  padding: 2px 4px;
}

.editable-cell:hover {
  background: #f5f5f5;
  border-radius: 4px;
}

.datatype-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
  min-height: 22px;
  padding: 2px 4px;
}

.datatype-trigger:hover {
  background: #f5f5f5;
  border-radius: 4px;
}

.datatype-text {
  line-height: 1;
}

.bool-true {
  color: @bool-true-color !important;
}

.bool-false {
  color: @bool-false-color !important;
}

.section-mapping :deep(td .bool-true) {
  color: @bool-true-color !important;
}

.section-mapping :deep(td .bool-false) {
  color: @bool-false-color !important;
}

.triangle {
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 6px solid #1f1f1f;
  display: inline-block;
  transform: translateY(1px);
}

.triangle-only {
  margin-left: 0;
}

/* —— 按钮区域 —— */
.actions-bar {
  position: static;
  bottom: auto;
  z-index: 1;
  background: transparent;
  border-top: none;
  box-shadow: none;
  display: flex;
  justify-content: center;
  gap: 16px;
  padding: 0;
  margin: 40px !important;
}

.btn-save,
.btn-cancel {
  min-width: 120px;
  height: 36px;
  border-radius: 999px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.06);
}

/* 工具类 */
.w-400 {
  width: 50% !important;
}

.w-120 {
  width: 120px;
}

.mt-10 {
  margin-top: 10px;
}

.mt-16 {
  margin-top: 16px;
}

/* 适配 */
@media (max-width: 992px) {
  .w-400 {
    width: 100% !important;
  }

  .inline-item :deep(.ant-form-item-control) {
    min-width: 100%;
  }

  .inline-item :deep(.ant-select) {
    width: 100% !important;
  }
}

@media (max-width: 480px) {
  .header-inner {
    padding: 0 12px;
  }

  .content-inner {
    padding: 10px 12px 0 12px;
  }
}

/* 生产源表格行高 */
.section-prod .custom-table {
  :deep(.ant-table-thead > tr > th) {
    height: 40px !important;
    padding-top: 10px !important;
    padding-bottom: 10px !important;
    line-height: 20px !important;
  }

  :deep(.ant-table-tbody > tr > td) {
    height: 40px !important;
    padding-top: 10px !important;
    padding-bottom: 10px !important;
    line-height: 20px !important;
  }
}

/* Mapping 配置表格行高 */
.section-mapping .mapping-table {
  :deep(.ant-table-thead > tr > th) {
    height: 40px !important;
    padding-top: 10px !important;
    padding-bottom: 10px !important;
    line-height: 20px !important;
  }

  :deep(.ant-table-tbody > tr > td) {
    height: 40px !important;
    padding-top: 10px !important;
    padding-bottom: 10px !important;
    line-height: 20px !important;
  }
}

/* 行置灰仅视觉（交互由逻辑控制） */
:deep(.ant-table-tbody > tr.row-disabled > td) {
  background: #fafafa !important;
  color: #9aa0a6 !important;
}
</style>
